//
//  testCBInventory.c
//  cbitcoin
//
//  Created by Matthew Mitchell on 11/07/2012.
//  Copyright (c) 2012 Matthew Mitchell
//  
//  This file is part of cbitcoin. It is subject to the license terms
//  in the LICENSE file found in the top-level directory of this
//  distribution and at http://www.cbitcoin.com/license.html. No part of
//  cbitcoin, including this file, may be copied, modified, propagated,
//  or distributed except according to the terms contained in the
//  LICENSE file.

#include <stdio.h>
#include "CBInventory.h"
#include <time.h>
#include "stdarg.h"

void CBLogError(char * format, ...);
void CBLogError(char * format, ...){
	va_list argptr;
    va_start(argptr, format);
    vfprintf(stderr, format, argptr);
    va_end(argptr);
	printf("\n");
}

int main(){
	unsigned int s = (unsigned int)time(NULL);
	s = 1337544566;
	printf("Session = %ui\n", s);
	srand(s);
	// Test deserialisation
	unsigned char data[109] = {
		0x03, // Three items
		0x01, 0x00, 0x00, 0x00, // Type transaction
		0xFB, 0x30, 0xB1, 0x9B, 0x3A, 0x0F, 0x82, 0x4A, 0xF1, 0x2B, 0x6E, 0xA4, 0x72, 0xBA, 0x9B, 0x3A, 0x67, 0xBA, 0xF2, 0xD8, 0x2E, 0x18, 0x06, 0x9D, 0x4A, 0x1B, 0x54, 0xA3, 0xD8, 0x9C, 0x43, 0xCF, // Hash
		0x02, 0x00, 0x00, 0x00, // Type block
		0xFA, 0x30, 0xB1, 0x9B, 0x35, 0x0F, 0x82, 0x4A, 0x92, 0x2B, 0x6E, 0xA4, 0x72, 0xBA, 0x9C, 0x3A, 0x67, 0xBA, 0xF2, 0x18, 0x12, 0x18, 0xF2, 0x9D, 0x4A, 0x1C, 0x54, 0xA3, 0xD8, 0x9C, 0x43, 0xBA, // Hash
		0x00, 0x00, 0x00, 0x00, // Type error
		0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Hash
	};
	CBByteArray * bytes = CBNewByteArrayWithDataCopy(data, 109);
	CBInventory * inv = CBNewInventoryFromData(bytes);
	if(CBInventoryDeserialise(inv) != 109){
		printf("DESERIALISATION LEN FAIL\n");
		return 1;
	}
	if (inv->itemNum != 3) {
		printf("DESERIALISATION NUM FAIL\n");
		return 1;
	}
	if (inv->itemFront->type != CB_INVENTORY_ITEM_TX) {
		printf("DESERIALISATION FIRST TYPE FAIL\n");
		return 1;
	}
	if (memcmp(CBByteArrayGetData(inv->itemFront->hash), (unsigned char []){0xFB, 0x30, 0xB1, 0x9B, 0x3A, 0x0F, 0x82, 0x4A, 0xF1, 0x2B, 0x6E, 0xA4, 0x72, 0xBA, 0x9B, 0x3A, 0x67, 0xBA, 0xF2, 0xD8, 0x2E, 0x18, 0x06, 0x9D, 0x4A, 0x1B, 0x54, 0xA3, 0xD8, 0x9C, 0x43, 0xCF}, 32)) {
		printf("DESERIALISATION FIRST HASH FAIL\n0x");
		unsigned char * d = CBByteArrayGetData(inv->itemFront->hash);
		for (int x = 0; x < 32; x++) {
			printf("%.2X", d[x]);
		}
		printf("\n!=\n0x");
		d = (unsigned char []){0xFB, 0x30, 0xB1, 0x9B, 0x3A, 0x0F, 0x82, 0x4A, 0xF1, 0x2B, 0x6E, 0xA4, 0x72, 0xBA, 0x9B, 0x3A, 0x67, 0xBA, 0xF2, 0xD8, 0x2E, 0x18, 0x06, 0x9D, 0x4A, 0x1B, 0x54, 0xA3, 0xD8, 0x9C, 0x43, 0xCF};
		for (int x = 0; x < 32; x++) {
			printf("%.2X", d[x]);
		}
		return 1;
	}
	if (inv->itemFront->next->type != CB_INVENTORY_ITEM_BLOCK) {
		printf("DESERIALISATION SECOND TYPE FAIL\n");
		return 1;
	}
	if (memcmp(CBByteArrayGetData(inv->itemFront->next->hash), (unsigned char []){0xFA, 0x30, 0xB1, 0x9B, 0x35, 0x0F, 0x82, 0x4A, 0x92, 0x2B, 0x6E, 0xA4, 0x72, 0xBA, 0x9C, 0x3A, 0x67, 0xBA, 0xF2, 0x18, 0x12, 0x18, 0xF2, 0x9D, 0x4A, 0x1C, 0x54, 0xA3, 0xD8, 0x9C, 0x43, 0xBA}, 32)) {
		printf("DESERIALISATION SECOND HASH FAIL\n0x");
		unsigned char * d = CBByteArrayGetData(inv->itemFront->next->hash);
		for (int x = 0; x < 32; x++) {
			printf("%.2X", d[x]);
		}
		printf("\n!=\n0x");
		d = (unsigned char []){0xFA, 0x30, 0xB1, 0x9B, 0x35, 0x0F, 0x82, 0x4A, 0x92, 0x2B, 0x6E, 0xA4, 0x72, 0xBA, 0x9C, 0x3A, 0x67, 0xBA, 0xF2, 0x18, 0x12, 0x18, 0xF2, 0x9D, 0x4A, 0x1C, 0x54, 0xA3, 0xD8, 0x9C, 0x43, 0xBA};
		for (int x = 0; x < 32; x++) {
			printf("%.2X", d[x]);
		}
		return 1;
	}
	if (inv->itemFront->next->next->type != CB_INVENTORY_ITEM_ERROR) {
		printf("DESERIALISATION THIRD TYPE FAIL\n");
		return 1;
	}
	if (memcmp(CBByteArrayGetData(inv->itemFront->next->next->hash), (unsigned char []){0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, 32)) {
		printf("DESERIALISATION THIRD HASH FAIL\n0x");
		unsigned char * d = CBByteArrayGetData(inv->itemFront->next->next->hash);
		for (int x = 0; x < 32; x++) {
			printf("%.2X", d[x]);
		}
		printf("\n!=\n0x");
		d = (unsigned char []){0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
		for (int x = 0; x < 32; x++) {
			printf("%.2X", d[x]);
		}
		return 1;
	}
	// Test serialisation with timestamps
	memset(CBByteArrayGetData(bytes), 0, 109);
	CBReleaseObject(inv->itemFront->hash);
	inv->itemFront->hash = CBNewByteArrayWithDataCopy((unsigned char []){0xFB, 0x30, 0xB1, 0x9B, 0x3A, 0x0F, 0x82, 0x4A, 0xF1, 0x2B, 0x6E, 0xA4, 0x72, 0xBA, 0x9B, 0x3A, 0x67, 0xBA, 0xF2, 0xD8, 0x2E, 0x18, 0x06, 0x9D, 0x4A, 0x1B, 0x54, 0xA3, 0xD8, 0x9C, 0x43, 0xCF}, 32);
	CBReleaseObject(inv->itemFront->next->hash);
	inv->itemFront->next->hash = CBNewByteArrayWithDataCopy((unsigned char []){0xFA, 0x30, 0xB1, 0x9B, 0x35, 0x0F, 0x82, 0x4A, 0x92, 0x2B, 0x6E, 0xA4, 0x72, 0xBA, 0x9C, 0x3A, 0x67, 0xBA, 0xF2, 0x18, 0x12, 0x18, 0xF2, 0x9D, 0x4A, 0x1C, 0x54, 0xA3, 0xD8, 0x9C, 0x43, 0xBA}, 32);
	CBReleaseObject(inv->itemFront->next->next->hash);
	inv->itemFront->next->next->hash = CBNewByteArrayWithDataCopy((unsigned char []){0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, 32);
	if (CBInventorySerialise(inv, true) != 109){
		printf("SERIALISATION LEN FAIL\n");
		return 1;
	}
	if (memcmp(data, CBByteArrayGetData(bytes), 109)) {
		printf("SERIALISATION FAIL\n0x");
		unsigned char * d = CBByteArrayGetData(bytes);
		for (int x = 0; x < 109; x++) {
			printf("%.2X", d[x]);
		}
		printf("\n!=\n0x");
		for (int x = 0; x < 109; x++) {
			printf("%.2X", data[x]);
		}
		return 1;
	}
	CBReleaseObject(inv);
	CBReleaseObject(bytes);
	// Test adding 50000 inventory items
	inv = CBNewInventory();
	for (int x = 0; x < 50000; x++) {
		CBByteArray * hash = CBNewByteArrayOfSize(32);
		CBByteArraySetInt16(hash, 0, x);
		if (!CBInventoryTakeInventoryItem(inv, CBNewInventoryItem((x % 2) ? CB_INVENTORY_ITEM_BLOCK : CB_INVENTORY_ITEM_TX, hash))){
			printf("TAKE INV ITEM FAIL AT %u", x);
			return 1;
		}
		CBReleaseObject(hash);
	}
	// Test serialising data
	bytes = CBNewByteArrayOfSize(CBInventoryCalculateLength(inv));
	int len = bytes->length;
	CBRetainObject(bytes);
	CBGetMessage(inv)->bytes = bytes;
	if (CBInventorySerialise(inv, true) != len){
		printf("2ND SERIALISATION LEN FAIL\n");
		return 1;
	}
	// Now try deserialisation
	CBReleaseObject(inv);
	inv = CBNewInventoryFromData(bytes);
	CBRetainObject(bytes);
	if(CBInventoryDeserialise(inv) != len){
		printf("2ND DESERIALISATION LEN FAIL\n");
		return 1;
	}
	if (inv->itemNum != 50000) {
		printf("2ND DESERIALISATION NUM FAIL\n");
		return 1;
	}
	for (int x = 0; x < 50000; x++) {
		CBInventoryItem * item = CBInventoryPopInventoryItem(inv);
		if (item->type != ((x % 2) ? CB_INVENTORY_ITEM_BLOCK : CB_INVENTORY_ITEM_TX)) {
			printf("GET ITEM TYPE FAIL AT %u", x);
			return 1;
		}
		if (CBByteArrayReadInt16(item->hash, 0) != x) {
			printf("GET ITEM HASH FAIL AT %u", x);
			return 1;
		}
	}
	if (CBInventoryPopInventoryItem(inv) != NULL) {
		printf("GET ITEM NULL FAIL");
		return 1;
	}
	CBReleaseObject(inv);
	return 0;
}
